<?php
declare(strict_types=1);
namespace Framework\Libs;

use Framework\Service;
use Framework\Support\MySqlDb;
use Framework\Support\RedisCache;
use Swoole\WebSocket\Server as WebSocketServer;
use Swoole\Http\Server as HttpServer;
use Swoole\Timer;
use Swoole\Table;
/**
 * //IDE_LOAD_START
 *
 * //IDE_LOAD_END
// replace  swoole_table_attr
 */
class Model extends \stdClass
{

    public MySqlDb $db;
    protected RedisCache $cache;
    protected Service $server;
    protected HttpServer|WebSocketServer $http_server;
    protected Event $event;
    protected array $add_rule = [];
    protected array $edit_rule = [];


	public string $link = 'db';
	public string $redis = 'cache';
	public string $table;
    // replace swoole_table_public

	public function __construct()
	{
		$this->server = $_ENV["bizan"];
		$this->http_server = $this->server->http_server;
        $this->event = new \App\Event();
        $this->db = $_ENV['db_class'][$this->link];
        $this->cache = $_ENV['cache_class'][$this->redis];

        // replace swoole_table
	}

	/**
	 * @param $name
	 * @return mixed
	 */
	public function __get($name)
	{
	    // replace models
	}

	public function auto_load()
	{


	}

	public function SetConfig($name,$isjson,$value){
	    switch ($isjson){
             case 1:
                  $value= xn_json_encode($value);
               break;
              case 2:
                   $value= implode(',',$value);
                break;
         }
        $data = $this->_T("Config")->set($name,['Json'=>$isjson,'Value'=>$value]);
        return $value;
    }

    public function GetConfig($name,$def='')
    {
        $data = $this->_T("Config")->get($name);
        $value = $def;
        if(empty($data)){
            return $value;
        }
        switch ($data['Json']){
            case 1:
                $value= xn_json_decode($data['Value']);
                break;
            case 2:
                $value= explode(',',$data['Value']);
                break;
            default:
                $value =  $data['Value'];
        }
        return $value;
    }


    /**
     * @param $name
     * @return \swoole\table
     */
    public function _T($name):Table
    {
       // replace table
    }

    function validate(array $data, $type = 'add')
    {
        // hook model_common_verify_start.php
        $field = [];
        switch ($type) {
            case 'add':
                $field = $this->add_rule;
                break;
            case 'edit':
                $field = $this->edit_rule;
                break;
            default:
                $field = $this->rule[$type]??[];
        }
        $return = true;
        try {
            foreach ($field as $k=> $v) {
                $func = !empty($this->rule[$v]['func']) ? $this->rule[$v]['func'] : "checkString";
                $is_success = false;
                if (method_exists($this, $func)) {
                    $is_success = $this->$func($data,$k, $v);
                }else if(function_exists($func)){
                    $is_success = $func($data, $k, $v);
                }
                if ($v['error'] && $is_success!=true) {
                    $this->response('0001',[],is_string($is_success)?$is_success:$v["message"]);
                } else {
                    $return = false;
                }
            }
        } catch (CustomException $err) {

        }
        // hook model_common_verify_end.php
        return $return;
    }

	public function CacheSet($k, $v, $life = 0)
	{
		return $this->cache->set($k, $v, $life);
	}
	//集合增加
	public function CacheSadd($k,...$value){
        return $this->cache->sadd($k, ...$value);
    }
    //返回第一个集合与其他集合之间的差异。
	public function CacheSDIFF($k,...$value){
        return $this->cache->SDIFF($k, ...$value);
    }

    //移除集合中一个或多个成员
	public function CacheSREM($k,...$value){
        return $this->cache->SREM($k, ...$value);
    }

    //集合的并集
	public function CacheSUNION($k,...$value){
        return $this->cache->SUNION($k, ...$value);
    }
    //集合的交集
    public function CacheSINTER($id)
    {
        return $this->cache->SINTER($id);
    }
    public function CacheInfo()
    {
        return $this->cache->info();
    }

    //集合所有元素
    public function CacheSMEMBERS($k){
        return $this->cache->SMEMBERS($k);
    }

    public function CacheSISMEMBER($k,$v){
        return $this->cache->SISMEMBER($k,$v);
    }

    public function CacheScard($k){
        return $this->cache->scard($k);
    }

    //SetBit
    public function CachesetBit($k,$o, $v, $life = 0){
        return $this->cache->setBit($k,$o, $v, $life);
    }

    //getBit
    public function CachegetBit($k,$o){
        return $this->cache->getBit($k,$o);
    }

    //bitCount
    public function CachebitCount($k){
        return $this->cache->bitCount($k);
    }

    //bitOp
    public function CachebitOp($k,$o,...$v){
        return $this->cache->bitOp($k,$o,...$v);
    }
    //bitPos
    public function CachebitPos($key, $bit, $start, $end = null){
        return $this->cache->bitPos($key, $bit, $start, $end);
    }

    public function CacheGet($k)
	{
		return $this->cache->get($k);
	}

	public function CacheKeys($k)
	{
		return $this->cache->keys($k);
	}

	public function CacheMGet($k)
	{
		return $this->cache->mget($k);
	}

	public function CacheHDel($k,$v)
	{
		return $this->cache->hdel($k,$v);
	}

	public function CacheMSet($kv,$life=0)
	{
		return $this->cache->mset($kv,$life);
	}

	public function CacheHMSet($k,$v,$life=0)
	{
		return $this->cache->hmset($k,$v,$life);
	}

	public function CacheHMGet($k,$v)
	{
		return $this->cache->hmget($k,$v);
	}

	public function CacheHGetAll($k)
	{
		return $this->cache->hgetAll($k);
	}

	public function CacheFlushAll()
	{
		return $this->cache->flushall();
	}

	public function CacheDel($k)
	{
		return $this->cache->delete($k);
	}

	public function CacheSelect($id)
	{
		return $this->cache->select($id);
	}

	public function CacheLlen($id)
	{
		return $this->cache->llen($id);
	}

	public function CacheLpop($id)
	{
		return $this->cache->lpop($id);
	}

	public function CacheRpush($k, $data)
	{
		return $this->cache->rpush($k, $data);
	}

	public function CacheLpush($k, $data)
	{
		return $this->cache->lpush($k, $data);
	}

	public function CacheLrange($id,$start=0,$end=-1)
	{
		return $this->cache->lrange($id,$start,$end);
	}

	public function CacheZIncrBy($k,$v,$member){
		return $this->cache->zincyby($k,$v,$member);
	}
    public function CacheHincrby ($k,$member,$v){
        return $this->cache->hincrby($k,$member,$v);
    }

	public function CachePfadd($k,$v){
		return $this->cache->pfadd($k,$v);
	}
	public function CachePfcount($k){
		return $this->cache->pfcount($k);
	}

	public function CachePfmerge($k,array $needMerge){
		return $this->cache->pfmerge($k,$needMerge);
	}


	public function CacheZrevrange($k,$start,$end){
		return $this->cache->zrevrange($k,$start,$end);
	}
	public function CacheZcard($k){
		return $this->cache->zcard($k);
	}
	public function CacheIncr($k){
		return $this->cache->incr($k);
	}
	public function CacheZscore($k,$member){
		return $this->cache->zscore($k,$member);
	}

	public function CacheExpire($k,$ttl){
		return $this->cache->expire($k,$ttl);
	}
	public function CacheExpireAt($k,$timestamp){
		return $this->cache->expireAt($k,$timestamp);
	}


	//GEOPOS：从 Key 里面返回所有给定位置对象的位置(经度和纬度)。
	public function CacheGeoPos($key = 'geo::user', ...$member)
	{
		return $this->cache->geopos($key, ...$member);
	}

	//GEOADD：将给定的位置对象(纬度、经度、名字)添加到指定的 Key。
	public function CacheGeoAdd($longitude, $latitude, $member, $key = 'geo::user')
	{
		return $this->cache->geoadd($longitude, $latitude, $member, $key);
	}

	//GEODIST：返回两个给定位置之间的距离。
	public function CacheGeoDist($member1, $member2, $unit = 'km', $key = 'geo::user')
	{
		return $this->cache->geodist($member1, $member2, $unit, $key);
	}

	//GEORADIUS：以给定的经纬度为中心，返回目标集合中与中心的距离不超过给定最大距离的所有位置对象。
	public function CacheGeoRadius($longitude, $latitude, $radius = 3, $unit = 'km', $with_dist = true, $sort = 'asc', $key = 'geo::user')
	{
		return $this->cache->georadius($longitude, $latitude, $radius, $unit, $with_dist, $sort, $key);
	}

	//GEORADIUSBYMEMBER：以给定的位置对象为中心，返回与其距离不超过给定最大距离的所有位置对象。
	public function CacheGeoRadiusMember($member, $radius = 3, $unit = 'km', $with_dist = true, $sort = 'asc', $key = 'geo::user')
	{
		return $this->cache->georadiusbymember($member, $radius, $unit, $with_dist, $sort, $key);
	}

	public function CacheTruncate()
	{
		return $this->cache->truncate();
	}

	public function show_tables($name,$db='')
    {
        $db = $db??$this->db;
        return $db->sql_find(['sql'=>'SHOW TABLE STATUS FROM ' . $name,'val'=>[]]);
    }
	public function show_status()
    {
        return $this->db->sql_find(['sql'=>'SHOW STATUS','val'=>[]]);
    }

	public function show_columns($table='')
	{
	    $table=$table??$this->table;
		if (empty($table)) {
			return false;
		}
		return $this->db->sql_find(['sql'=>'SHOW FULL COLUMNS FROM ' . $table,'val'=>[]]);
	}

	public function optimize($table='')
	{
	    $table=$table??$this->table;
		if (empty($table)) {
			return false;
		}
		return $this->db->sql_find(['sql'=>'OPTIMIZE TABLE ' . $table,'val'=>[]]);
	}

	public function query($sql,$val=[])
	{
		return $this->db->sql_find(['sql'=>$sql,'val'=>$val]);
	}

    public function sql_find($sql,$val=[])
    {
        return $this->db->sql_find(['sql'=>$sql,'val'=>$val]);
    }

	public function sql_find_one($sql,$val=[])
	{
		return $this->db->sql_find_one(['sql'=>$sql,'val'=>$val]);
	}

	public function exec($sql,$val=[])
	{
		return $this->db->exec(['sql'=>$sql,'val'=>$val]);
	}

	public function truncate(): bool|int
    {
		return !empty($this->table) ? $this->db->truncate($this->table):true;
	}



    // manager response 需要 controller 自行捕获
	public function response($code,$data, $result)
	{
		//is_array($result) && $result= xn_json_encode($result);
        throw new CustomException($result, 10000+$code,$data);
	}


	public function sendMessage(string $cmd,$value=[]){
        $n = (int)$this->http_server->setting['worker_num'] + (int)$this->http_server->setting['task_worker_num'];
        $class = substr(get_called_class(),4,-3);
        for($i=0;$i<$n;$i++){
            $this->http_server->worker_id!=$i && $this->http_server->sendMessage(['cmd'=>$cmd,'class'=>$class,'value'=>$value],$i);
        }
    }
	//投递异步任务
	public function PostTask($action, $data, $_controller = 'Task'): void
    {
        \Swoole\Coroutine::create(function () use($action,$data,$_controller){
            $this->http_server->task([
                'controller' => $_controller,
                'action' => $action,
                'data' => $data,
            ]);
		});
	}
}

?>